/***************************************************************************
 *  Title: Input 
 * -------------------------------------------------------------------------
 *    Purpose: Handles the input from stdin
 *    Author: Stefan Birrer
 *    Version: $Revision: 1.4 $
 *    Last Modification: $Date: 2009/10/12 20:50:12 $
 *    File: $RCSfile: interpreter.c,v $
 *    Copyright: (C) 2002 by Stefan Birrer
 ***************************************************************************/
/***************************************************************************
 *  ChangeLog:
 * -------------------------------------------------------------------------
 *    $Log: interpreter.c,v $
 *    Revision 1.4  2009/10/12 20:50:12  jot836
 *    Commented tsh C files
 *
 *    Revision 1.3  2009/10/11 04:45:50  npb853
 *    Changing the identation of the project to be GNU.
 *
 *    Revision 1.2  2008/10/10 00:12:09  jot836
 *    JSO added simple command line parser to interpreter.c. It's not pretty
 *    but it works. Handles quoted strings, and preserves backslash behavior
 *    of bash. Also, added simple skeleton code as well as code to create and
 *    free commandT structs given a command line.
 *
 *    Revision 1.1  2005/10/13 05:24:59  sbirrer
 *    - added the skeleton files
 *
 *    Revision 1.4  2002/10/24 21:32:47  sempi
 *    final release
 *
 *    Revision 1.3  2002/10/21 04:47:05  sempi
 *    Milestone 2 beta
 *
 *    Revision 1.2  2002/10/15 20:37:26  sempi
 *    Comments updated
 *
 *    Revision 1.1  2002/10/15 20:20:56  sempi
 *    Milestone 1
 *
 ***************************************************************************/
#define __INTERPRETER_IMPL__

/************System include***********************************************/
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "string.h"

/************Private include**********************************************/
#include "interpreter.h"
#include "io.h"
#include "runtime.h"
#include "stringtokenizer.h"

/************Defines and Typedefs*****************************************/
/*  #defines and typedefs should have their names in all caps.
 *  Global variables begin with g. Global constants with k. Local
 *  variables should be in all lower case. When initializing
 *  structures and arrays, line everything up in neat columns.
 */
typedef struct string_l
{
  char* s;
  struct string_l* next;
} stringL;

int MAXARGS = 100;

/**************Function Prototypes******************************************/

commandT*
getCommand(char* cmdLine);

void
freeCommand(commandT* cmd);

/**************Implementation***********************************************/


/*
 * Interpret
 *
 * arguments:
 *   char *cmdLine: pointer to the command line string
 *
 * returns: none
 *
 * This is the high-level function called by tsh's main to interpret a
 * command line.
 */
void
Interpret(char* cmdLine)
{
  struct StringTokens * tokens = tokens_create( cmdLine, "|" );

  if ( tokens_size( tokens ) == 1 )
  {
    size_t numLeft = 0;
    size_t numRight = 0;

    if ( strchr( tokens_get( tokens, 0 ), '<' ) != NULL )
      numLeft++;
    
    if ( strchr( tokens_get( tokens, 0 ), '>' ) != NULL )
      numRight++;

    if ( numLeft == 0 && numRight == 1 ) {
      struct StringTokens * tok2 = tokens_create( tokens_get( tokens, 0 ), ">" );
      char * tmp1 = strdup( tokens_get(tok2, 0) );
      char * tmp2 = strdup( tokens_get(tok2, 1) );

      commandT* cmd = getCommand(tmp1);
      
      RunCmdRedirOut(cmd, tmp2);

      free(tmp1);
      free(tmp2);
      freeCommand(cmd);
      tokens_free(tok2);
    } else if ( numRight == 0 && numLeft == 1 ) {
      struct StringTokens * tok2 = tokens_create( tokens_get( tokens, 0 ), "<" );
      char * tmp1 = strdup( tokens_get(tok2, 0) );
      char * tmp2 = strdup( tokens_get(tok2, 1) );

      commandT* cmd = getCommand(tmp1);
      
      RunCmdRedirIn(cmd, tmp2);

      free(tmp1);
      free(tmp2);
      freeCommand(cmd);
      tokens_free(tok2);

    } else if ( numLeft == 1 && numRight == 1 ) {
      struct StringTokens * tok2 = tokens_create( tokens_get( tokens, 0 ), "<>" );
      char * tmp1 = strdup( tokens_get(tok2, 0) );
      char * tmp2 = strdup( tokens_get(tok2, 1) );
      char * tmp3 = strdup( tokens_get(tok2, 2) );

      commandT* cmd = getCommand(tmp1);
      
      RunCmdRedirAll(cmd, tmp2, tmp3);

      free(tmp1);
      free(tmp2);
      free(tmp3);
      freeCommand(cmd);
      tokens_free(tok2);
    } else if ( numLeft == 0 && numRight == 0 ) {
      char * tmp = strdup(tokens_get(tokens, 0));
      commandT* cmd = getCommand( tmp );
    
      // An ampersand as the final argument tells us
      // to run this command in the background
      if (cmd->background)
        {
          RunCmdBg(cmd);
        }
      else
        {
          RunCmd(cmd);
        }
      freeCommand(cmd);
      free(tmp);
    }
  }

  if ( tokens_size( tokens ) > 1 )
  {
     char * temp[128];
     commandT* cmd[128];     

     size_t i;
     for ( i = 0; i < tokens_size( tokens ); i++ )
     {
       temp[i] = strdup( tokens_get(tokens, i) );
       cmd[i] = getCommand( temp[i] );
     }

     RunCmdPipe(cmd, tokens_size( tokens ) );

     for ( i = 0; i < tokens_size( tokens ); i++ )
     {
       free( temp[i] );
       freeCommand( cmd[i] );
     }     
  }

  tokens_free( tokens );
} /* Interpret */


/*
 * getCommand
 *
 * arguments:
 *   char *cmdLine: pointer to the command line string
 *
 * returns: commandT*: pointer to the commandT struct generated by
 *                     parsing the cmdLine string
 *
 * This parses the command line string, and returns a commandT struct,
 * as defined in runtime.h.  You must free the memory used by commandT
 * using the freeCommand function after you are finished.
 *
 * This function tokenizes the input, preserving quoted strings. It
 * supports escaping quotes and the escape character, '\'.
 */
commandT*
getCommand(char* cmdLine)
{
  commandT* cmd = malloc(sizeof(commandT) + sizeof(char*) * MAXARGS);
  cmd->argv[0] = 0;
  cmd->name = 0;
  cmd->argc = 0;
  cmd->cmdLine = 0;
  cmd->background = FALSE;
  cmd->path = NULL;

  int i, inArg = 0;
  char quote = 0;
  char escape = 0;

  cmd->cmdLine = malloc(sizeof(char*) * BUFSIZE);
  strcpy(cmd->cmdLine, cmdLine);

  // Set up the initial empty argument
  char* tmp = malloc(sizeof(char*) * BUFSIZE);
  int tmpLen = 0;
  tmp[0] = 0;

  //printf("parsing:%s\n", cmdLine);

  for (i = 0; cmdLine[i] != 0; i++)
    {
      //printf("\tindex %d, char %c\n", i, cmdLine[i]);

      // Check for whitespace
      if (cmdLine[i] == ' ')
        {
          if (inArg == 0)
            continue;
          if (quote == 0)
            {
              // End of an argument
              //printf("\t\tend of argument %d, got:%s\n", cmd.argc, tmp);
              cmd->argv[cmd->argc] = malloc(sizeof(char) * (tmpLen + 1));
              strcpy(cmd->argv[cmd->argc], tmp);

              inArg = 0;
              tmp[0] = 0;
              tmpLen = 0;
              cmd->argc++;
              cmd->argv[cmd->argc] = 0;
              continue;
            }
        }

      // If we get here, we're in text or a quoted string
      inArg = 1;

      // Start or end quoting.
      if (cmdLine[i] == '\'' || cmdLine[i] == '"')
        {
          if (escape != 0 && quote != 0 && cmdLine[i] == quote)
            {
              // Escaped quote. Add it to the argument.
              tmp[tmpLen++] = cmdLine[i];
              tmp[tmpLen] = 0;
              escape = 0;
              continue;
            }

          if (quote == 0)
            {
              //printf("\t\tstarting quote around %c\n", cmdLine[i]);
              quote = cmdLine[i];
              continue;
            }
          else
            {
              if (cmdLine[i] == quote)
                {
                  //printf("\t\tfound end quote %c\n", quote);
                  quote = 0;
                  continue;
                }
            }
        }

      // Handle escape character repeat
      if (cmdLine[i] == '\\' && escape == '\\')
        {
          escape = 0;
          tmp[tmpLen++] = '\\';
          tmp[tmpLen] = 0;
          continue;
        }

      // Handle single escape character followed by a non-backslash or quote character
      if (escape == '\\')
        {
          if (quote != 0)
            {
              tmp[tmpLen++] = '\\';
              tmp[tmpLen] = 0;
            }
          escape = 0;
        }

      // Set the escape flag if we have a new escape character sequence.
      if (cmdLine[i] == '\\')
        {
          escape = '\\';
          continue;
        }

      tmp[tmpLen++] = cmdLine[i];
      tmp[tmpLen] = 0;
    }
  // End the final argument, if any.
  if (tmpLen > 0)
    {
      //printf("\t\tend of argument %d, got:%s\n", cmd.argc, tmp);
      cmd->argv[cmd->argc] = malloc(sizeof(char) * (tmpLen + 1));
      strcpy(cmd->argv[cmd->argc], tmp);
      cmd->argc++;
      cmd->argv[cmd->argc] = 0;

      inArg = 0;
      tmp[0] = 0;
      tmpLen = 0;
    }

  free(tmp);

  // An ampersand as the final argument tells us
  // to run this command in the background
  if ( cmd->argc > 0 && cmd->argv[cmd->argc - 1][0] == '&' )
    {
      cmd->background = TRUE;
      cmd->argc--;
      free(cmd->argv[cmd->argc]);
      cmd->argv[cmd->argc] = 0;
    }

  cmd->name = cmd->argv[0];

  return cmd;
} /* getCommand */


/*
 * freeCommand
 *
 * arguments:
 *   commandT *cmd: pointer to the commandT struct to be freed
 *
 * returns: none
 *
 * This function frees all the memory associated with the given
 * commandT struct, before freeing the struct's memory itself.
 */
void
freeCommand(commandT* cmd)
{
  int i;

  if ((cmd->path != NULL) && (cmd->path != cmd->name))
    {
      free(cmd->path);
    }

  if (cmd->cmdLine != NULL)
    {
      free(cmd->cmdLine);
    }

  cmd->path = 0;
  cmd->cmdLine = 0;
  cmd->name = 0;

  for (i = 0; cmd->argv[i] != 0; i++)
    {
      free(cmd->argv[i]);
      cmd->argv[i] = 0;
    }

  free(cmd);
} /* freeCommand */
